// Trippr.cpp : Defines the entry point for the application.
//

#define FLICKR_API_KEY _T("REPLACE_THIS_WITH_YOUR_FLICKR_KEY")

#include <windows.h>
#include <aygshell.h>
#include <htmlctrl.h>
#include <connmgr.h>
#include <msxml.h>
#include <objsafe.h>

#include "Trippr.h"
#include "GPS.h"

// Checks if a valid network connection is present that
// will enable us to access the url 'pszUrl'. If one is
// not present it will attempt to establish one.
static HANDLE CreateNetworkConnection(LPCTSTR pszUrl)
{
	CONNMGR_CONNECTIONINFO ci = {0};
	HANDLE hConnection = NULL;
	DWORD dwStatus = 0;
	HRESULT hr; 

	// Register with the connection manager
	ci.cbSize = sizeof(CONNMGR_CONNECTIONINFO); 
	ci.dwParams = CONNMGR_PARAM_GUIDDESTNET; 
	ci.dwFlags = CONNMGR_FLAG_PROXY_HTTP; 
	ci.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE; 

	// Ask connection manager which network would best
	// enable us to connect to the URL we aim to use.
	hr = ConnMgrMapURL(pszUrl, &ci.guidDestNet, &dwStatus);
	if (hr != S_OK)
		return NULL;

	// Then request connection manager to establish a
	// connection to that network.
	hr = ConnMgrEstablishConnectionSync(&ci, &hConnection, 25000, &dwStatus); 
	if (hr != S_OK)
		return NULL;

	return hConnection;
}

static void CloseNetworkConnection(HANDLE hConnection)
{
   if (hConnection)
   {
	   // Tell connection manager we don't need access to the
	   // network anymore. Notice that this may not immediatly
	   // disconnect the network connection, as connection manager
	   // may have other clients that concurrently desire access,
	   // or it may decide to "cache" the connection in case
	   // another application wants to access the same netowrk in
	   // the near future.
       ConnMgrReleaseConnection(hConnection, TRUE);
   }
}

// Generate a URL to get a thumbnail for a particular Flickr image.
// pszThumbnailUrl is the buffer to store the URL in, pXMLNode is
// the current photo element within the XML response from Flickr.
static void GenerateThumbnailUrl(LPTSTR pszThumbnailURL, IXMLDOMNode * pXMLNode)
{
	// Flickr documents how to take the various photo attributes to
	// generate a unique URL to get a jpeg image. This is documented at
	// http://www.flickr.com/services/api/misc.urls.html and means
	// we can get a thumbnail of a flickr image by using a URL in the
	// following structure (subsituting things within {}'s with
	// the values specified in the XML document).
	//
	// http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_t.jpg
	
	IXMLDOMNamedNodeMap * pAttributes = NULL;
	HRESULT hr;

	_tcscpy(pszThumbnailURL, _T(""));

	// Get all the attributes associated with the
	// current element within the XML document
	hr = pXMLNode->get_attributes(&pAttributes);
	if (hr == S_OK && pAttributes != NULL)
	{
		BSTR bstrFarmId, bstrServerId, bstrId, bstrSecret;

		// Extract the current values for the various
		// attributes we need in order to build the image URL
		BSTR bstrName = SysAllocString(_T("farm"));
		IXMLDOMNode *pXMLAttributeNode;
		pAttributes->getNamedItem(bstrName, &pXMLAttributeNode);
		pXMLAttributeNode->get_text(&bstrFarmId);
		pXMLAttributeNode->Release();

		bstrName = SysAllocString(_T("server"));
		pAttributes->getNamedItem(bstrName, &pXMLAttributeNode);
		pXMLAttributeNode->get_text(&bstrServerId);
		pXMLAttributeNode->Release();

		bstrName = SysAllocString(_T("id"));
		pAttributes->getNamedItem(bstrName, &pXMLAttributeNode);
		pXMLAttributeNode->get_text(&bstrId);
		pXMLAttributeNode->Release();

		bstrName = SysAllocString(_T("secret"));
		pAttributes->getNamedItem(bstrName, &pXMLAttributeNode);
		pXMLAttributeNode->get_text(&bstrSecret);
		pXMLAttributeNode->Release();

		// Finally now that we have all the attribute values
		// we can generate the actual URL using the format
		// mentioned above.
		_stprintf(pszThumbnailURL, _T("http://farm%s.static.flickr.com/%s/%s_%s_t.jpg"),
			bstrFarmId, bstrServerId, bstrId, bstrSecret);

		SysFreeString(bstrFarmId);
		SysFreeString(bstrServerId);
		SysFreeString(bstrId);
		SysFreeString(bstrSecret);

		pAttributes->Release();
		pAttributes = NULL;
	}
}

static void CallWebService(HWND hDlg)
{
	// See http://www.flickr.com/services/api/flickr.photos.search.html
	// for documentation on this REST style API.

	GPS_POSITION gpsPosition;
	GPS_GetData(&gpsPosition, NULL);

	if ((gpsPosition.dwValidFields & GPS_VALID_LATITUDE)
		&& (gpsPosition.dwValidFields & GPS_VALID_LONGITUDE))
	{
		BOOL bAddedImages = FALSE;

		double minimumLongitude = gpsPosition.dblLongitude - 1;
		double minimumLatitude = gpsPosition.dblLatitude - 1;
		double maximumLongitude = minimumLongitude + 2;
		double maximumLatitude = minimumLatitude + 2;

		// Determine the URL needed to get list of photos within
		// a specific geographic region centered around the
		// user's current position
		TCHAR szUrl[MAX_PATH];
		_stprintf(szUrl, _T("http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=%s&sort=interestingness-asc&bbox=%f,%f,%f,%f&per_page=10"),
			FLICKR_API_KEY,
			minimumLongitude, minimumLatitude,
			maximumLongitude, maximumLatitude);

		// Ensure we have a suitable connection to the network
		HANDLE hConnection = CreateNetworkConnection(szUrl);
		if (!hConnection)
		{
			MessageBox(GetForegroundWindow(), _T("Failed to connect to the internet"), _T("Error"), MB_ICONERROR | MB_OK);
			return;
		}

		// Create an XML Document instance
   		IXMLDOMDocument2 *pDoc = NULL;
		HRESULT hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument2, (void**)&pDoc);
		if (hr == S_OK)
		{
			// Access the Flickr REST API and load the response into
			// the DOM Document. MSXML is able to fetch data from
			// HTTP URLs directly.
			VARIANT_BOOL bSuccess = VARIANT_FALSE;
			VARIANT filename;

			VariantInit(&filename);
			V_BSTR(&filename) = SysAllocString(szUrl);
			V_VT(&filename) = VT_BSTR;

			pDoc->put_async(VARIANT_FALSE);

			hr = pDoc->load(filename, &bSuccess);
			if (hr == S_OK && bSuccess == VARIANT_TRUE)
			{
				TCHAR szHTML[2048];
				TCHAR szThumbnailURL[2048];
				IXMLDOMNodeList *pNodeList = NULL;

				// Find all the photo elements within the XML response
				BSTR bstr = SysAllocString(_T("/rsp/photos/photo"));
				hr = pDoc->selectNodes(bstr, &pNodeList);
				if (hr == S_OK && pNodeList != NULL)
				{
					IXMLDOMNode * pXMLNode;
					
					while ((hr = pNodeList->nextNode(&pXMLNode)) == S_OK)
					{
						// And for each one generate an HTML snippet
						// consisting of an IMG tag referencing the
						// image on the flickr server(s).
						GenerateThumbnailUrl(szThumbnailURL, pXMLNode);
						_stprintf(szHTML, _T("<img src=\"%s\" style=\"border: 1px black solid; padding: 0px; margin: 1px\" />"), szThumbnailURL);

						// And append it to the end of the web browser control
						SendDlgItemMessage(hDlg, IDC_HTMLVIEW, DTM_ADDTEXTW, 0, (LPARAM)szHTML);

						pXMLNode->Release();
						bAddedImages = TRUE;
     				}

					pNodeList->Release();
				}
			}

			pDoc->Release();
			pDoc = NULL;
		}

		CloseNetworkConnection(hConnection);

		if (!bAddedImages)
		{
			SendDlgItemMessage(hDlg, IDC_HTMLVIEW, DTM_ADDTEXTW, 0, (LPARAM)_T("<h1>No images have been found</h1>"));
		}
	}
	else
	{
		SendDlgItemMessage(hDlg, IDC_HTMLVIEW, DTM_ADDTEXTW, 0, (LPARAM)_T("<h1>Current GPS position could not be determined</h1>"));
	}


	// Tell the web browser control that we've finished loading
	// the new content
	SendDlgItemMessage(hDlg, IDC_HTMLVIEW, DTM_ENDOFSOURCE, 0, 0);
}

// Message handler for Trippr Dialog.
INT_PTR CALLBACK TripprDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG:
            {
                // Create a Done button and size it.  
                SHINITDLGINFO shidi;
                shidi.dwMask = SHIDIM_FLAGS;
                shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
                shidi.hDlg = hDlg;
                SHInitDialog(&shidi);

				// Create a menu bar
				SHMENUBARINFO mbi;
                memset(&mbi, 0, sizeof(SHMENUBARINFO));
				mbi.cbSize     = sizeof(SHMENUBARINFO);
				mbi.hwndParent = hDlg;
				mbi.nToolBarId = IDR_MENU;
				mbi.hInstRes   = GetModuleHandle(NULL);
				mbi.dwFlags = SHCMBF_HMENU | SHCMBF_HIDESIPBUTTON;
	            SHCreateMenuBar(&mbi);

				// Pretend the user selected the Start GPS menu item
				// so that the application starts off with the GPS
				// enabled
				PostMessage(hDlg, WM_COMMAND, IDM_GPS_START, 0);
            }
            return (INT_PTR)TRUE;

		case WM_INITMENUPOPUP:
			// The menu is about to popup so enable/disable
			// the various menu items depending upon the state
			// of the application.
			if (GPS_IsOpened())
			{
				EnableMenuItem((HMENU)wParam, IDM_GPS_START, MF_BYCOMMAND | MF_GRAYED);
				EnableMenuItem((HMENU)wParam, IDM_GPS_STOP,  MF_BYCOMMAND | MF_ENABLED);
			}
			else
			{
				EnableMenuItem((HMENU)wParam, IDM_GPS_START, MF_BYCOMMAND | MF_ENABLED);
				EnableMenuItem((HMENU)wParam, IDM_GPS_STOP,  MF_BYCOMMAND | MF_GRAYED);
			}
			break;

        case WM_COMMAND:
            switch (LOWORD(wParam))
			{
				case IDOK:
		            EndDialog(hDlg, LOWORD(wParam));
					return (INT_PTR)TRUE;

				case IDM_GPS_START:
					if (!GPS_IsOpened())
					{
						GPS_Open();
					}
					break;
           
				case IDM_GPS_STOP:
					if (GPS_IsOpened())
					{
						GPS_Close();
					}
					break;

				case IDM_UPDATE:
					{
						HCURSOR hCursor = SetCursor(LoadCursor(GetModuleHandle(NULL), IDC_WAIT));
						CallWebService(hDlg);
						SetCursor(hCursor);
					}
					break;
            }
            break;

		case WM_SIZE:
			{
				// Resize the HTML control to cover the entire
				// client area
				HWND hwndHTML = GetDlgItem(hDlg, IDC_HTMLVIEW);
				SetWindowPos(hwndHTML, NULL, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER);
			}
			break;

        case WM_CLOSE:
            EndDialog(hDlg, message);
            return (INT_PTR)TRUE;

    }
    return (INT_PTR)FALSE;
}

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPTSTR    lpCmdLine,
                   int       nCmdShow)
{
	CoInitializeEx(NULL, COINIT_MULTITHREADED);
	InitHTMLControl(hInstance);
	
	return DialogBox(hInstance, (LPCTSTR)IDD_TRIPPR, NULL, TripprDialog);
}
